Skip to content

Intl: Add a new IntlListFormatter class #18519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

BogdanUngureanu
Copy link
Contributor

This PR adds support for ICU's ListFormatter implementation by implementing a new IntlListFormatter class in PHP.

The class supports ANDs, ORs and UNIT format types. However, they are only available if ICU version is 67 or above. On older versions of ICU, we only allow TYPE_AND and WIDTH_WIDE - we need this because, from what I've understood, the minimum version ICU supported is 57, which was bumped from 50 last year.

The class API is pretty simple:

$formatter = new IntlListFormatter('EN_US', IntlListFormatter::TYPE_AND, IntlListFormatter::WIDTH_WIDE);
echo $formatter->format([1,2,3]);

will display

1, 2, and 3

I have some questions here:

  • Do I need to create an RFC for this new addition?
  • I haven't used a namespace and used Intl prefix for consistency sake. Should it be behind a Intl namespace? I haven't seen a class that uses it so I thought I should go with the prefix.

@TimWolla
Copy link
Member

TimWolla commented May 8, 2025

Do I need to create an RFC for this new addition?

I wouldn't need an RFC if this is a straight forward addition to the existing Intl API which just maps to ICU in a straight forward fashion. I would like to see some smaller improvements, though:

  • Making the class final and /** @not-serializable */. This allows you to avoid the “not properly constructed” checks.
  • /** @strict-properties */ to further lock down the class against misuse.

I haven't used a namespace and used Intl prefix for consistency sake. Should it be behind a Intl namespace? I haven't seen a class that uses it so I thought I should go with the prefix.

The prefix makes sense to me. Using the Intl namespace should properly happen when designing an Intl API that isn't just a straight forward mapping to ICU, but also provides a nice API (e.g. enums, proper class and exception hierarchy, …).

@BogdanUngureanu
Copy link
Contributor Author

Hi @TimWolla! Thanks for the suggestions - great ideas! I've pushed a commit that makes the class final and enabled those flags.

Copy link
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more Stub nits. Please do not add PHPDoc comments, unless they allow you to express something that the types themselves cannot.

Comment on lines +5 to +6
/** @not-serializable */
/** @strict-properties */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @not-serializable */
/** @strict-properties */
/**
* @not-serializable
* @strict-properties
*/

To make it more clear that these two belong together.

/** @strict-properties */
final class IntlListFormatter {

/** @cvalue ULISTFMT_TYPE_OR */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @cvalue ULISTFMT_TYPE_OR */
/** @cvalue ULISTFMT_TYPE_OR */

/** @cvalue ULISTFMT_TYPE_OR */
public const int TYPE_OR = UNKNOWN;

/** @cvalue ULISTFMT_TYPE_UNITS */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** @cvalue ULISTFMT_TYPE_UNITS */
/** @cvalue ULISTFMT_TYPE_UNITS */

Comment on lines +27 to +31
/**
* @param string $locale
* @param int $type
* @param int $width
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @param string $locale
* @param int $type
* @param int $width
*/

Comment on lines +41 to +43
/**
* @return int
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @return int
*/

Comment on lines +46 to +48
/**
* @return string
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @return string
*/

Comment on lines +34 to +38
/**
* @param array $strings
*
* @return string|false
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* @param array $strings
*
* @return string|false
*/

* @return string
*/
public function getErrorMessage(): string {}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline at EOF.

@BogdanUngureanu
Copy link
Contributor Author

Some more Stub nits. Please do not add PHPDoc comments, unless they allow you to express something that the types themselves cannot.

Thanks for the quick response! I'll make the changes tomorrow. :)

}

UErrorCode status = U_ZERO_ERROR;
if (U_ICU_VERSION_MAJOR_NUM >= 67) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#if U_ICU_VERSION_MAJOR_NUM >= 67

UErrorCode status = U_ZERO_ERROR;
if (U_ICU_VERSION_MAJOR_NUM >= 67) {

LISTFORMATTER_OBJECT(obj) = ulistfmt_openForType(ZSTR_VAL(locale), type, width, &status);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is understood recent releases are more flexible however you do need to check type ... width does not contain silly values and if so throwing a ValueException (zend_argument_value_error with proper argument offset) then adding tests with silly values :)

@devnexen
Copy link
Member

devnexen commented May 8, 2025

windows CI failure is related otherwise

zend_long type = ULISTFMT_TYPE_AND;
zend_long width = ULISTFMT_WIDTH_WIDE;
ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STR(locale)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to double check locale, e.g. ZSTR_LEN(locale) needs to be <= INTL_MAX_LOCALE_LEN

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to expand on that if its len is 0 then locale can fetch a default from intl_locale_get_default(). Might be simpler if locale is just char * then, just an advice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants